home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / memstr.com / MEMSTR.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-05-09  |  10.0 KB  |  301 lines

  1. { ========================================================================= }
  2. PROGRAM MemStr;
  3.  
  4. { Version 8906.01 }
  5. { Written in Turbo Pascal, Version 5.0 }
  6. { Turbo Pascal is a product of Borland International. }
  7. { Turbo Professional is a product of TurboPower Software
  8. { ========================================================================= }
  9.  
  10. {$R-}    {Range checking off}
  11. {$B+}    {Boolean complete evaluation on}
  12. {$S+}    {Stack checking on}
  13. {$I+}    {I/O checking on}
  14. {$N+,E+} {Simulate numeric coprocessor}
  15. {$M 65500,16384,655360} {Turbo 3 default stack and heap}
  16. {$V-}    {Variable range checking off}
  17.  
  18. { ========================================================================= }
  19. (*
  20. This program uses the TPDOS and TPCRT units of Turbo Professional from
  21. Turbo Power Software.  If you do not have Turbo Professional, change
  22. the USES declarations to DOS and CRT and take out the call to the
  23. ExistAnyFile procedure.
  24. *)
  25. { ========================================================================= }
  26.  
  27. USES
  28.   TpDos,                  { Turbo Professional unit }
  29.   TpCrt;                  { Turbo Professional unit }
  30.  
  31.  
  32. TYPE
  33.   String12       = String [12];
  34.  
  35. { ========================================================================= }
  36. { superstring routines }
  37. { ========================================================================= }
  38.  
  39. (*
  40. MEMSTR.PAS is a demonstration of a technique for storing a file of
  41. strings in a memory array at runtime.
  42.  
  43. I was working on a program which needed to make random choices of words.
  44. For speed purposes, I needed to hold all the different words in memory,
  45. but at the same time, I also needed to store the words in ASCII files
  46. that I would maintain with a word processor.  I did not want to have to
  47. recompile the program every time I added or deleted a choice from a file.
  48. To load all the different selections into memory, no matter how many
  49. there were in the file, meant declaring an array at runtime.  I did this
  50. by setting aside memory on the heap and declaring pointers to that memory.
  51.  
  52. HOW IT WORKS:
  53.  
  54. The trick lies in declaring two types that are larger than the maximum
  55. amount of data you ever expect to use.  SuperArray and SuperCount are
  56. deliberately oversized arrays.  SuperArray is the maximum size array of
  57. characters possible in Turbo Pascal 5.0.  SuperCount is more entries
  58. than I expect to access.  If I ever need to access more than 1024 separate
  59. items, all I have to do is increase the upper range of SuperCount.
  60.  
  61. The program then declares two pointer types to access these array types and
  62. then a SuperString type which is a record containing both pointer types and
  63. a Size variable to log the amount of data stored in the SuperArray.  The
  64. total number of entries is stashed in ArrayPtr^ [0].  Any individual
  65. SuperString will be marked by only six bytes, pointing to two areas of
  66. heap, set aside at runtime.
  67. *)
  68.  
  69. { ========================================================================= }
  70.  
  71. TYPE
  72.   SuperArray     = Array [1 .. 65535] of char;   { max array size }
  73.   SuperCount     = Array [0 .. 1024] of word;    { max num of entries }
  74.   EntryPtrType   = ^SuperArray;
  75.   ArrayPtrType   = ^SuperCount;
  76.  
  77.   SuperString    = Record
  78.                      EntryPtr : EntryPtrType;
  79.                      ArrayPtr : ArrayPtrType;
  80.                      Size     : Word;
  81.                    End;
  82.  
  83. { ========================================================================= }
  84.  
  85. FUNCTION ExistAnyFile (FileName : String12) : Boolean;
  86. { Checks to see if a file exists before accessing it. }
  87.  
  88. VAR
  89.   SaveMode : Byte;
  90.  
  91. BEGIN
  92.   SaveMode := FileMode;
  93.   FileMode := 0;
  94.   ExistAnyFile := ExistFile (FileName);          { TpDos function }
  95.   FileMode := SaveMode;
  96. END;
  97.  
  98. { ========================================================================= }
  99.  
  100. (*
  101. The BuildSuperString procedure needs to be run once to initialize
  102. the superstring.
  103.  
  104. It reads the file, counting the entries and totalling their lengths;
  105. then it reserves the appropriate amount of space on the heap and assigns
  106. the starting locations to ArrayPtr^ and EntryPtr^, using Turbo's GetMem
  107. procedure.
  108.  
  109. Then it resets the file, reads the entries again and stashes each entry
  110. in EntryPtr^ and its starting location (relative to EntryPtr^ [1]) in
  111. ArrayPtr^.  It would be more efficient if the program actually stored a
  112. pointer to the actual address.  This improvement is left as an exercise
  113. for someone who is more proficient in pointer arithmetic than I am.
  114. However there is an advantage in this method in that I save one byte of
  115. data for every entry, because I am not bothering to store the length byte
  116. of the individual strings;  it isn't necessary.
  117. *)
  118.  
  119. { ========================================================================= }
  120.  
  121. PROCEDURE BuildSuperString (FileName  : String12;
  122.                             VAR Super : SuperString);
  123.  
  124. VAR
  125.   ReadStr       : String;
  126.   ReadFile      : Text;
  127.   Count         : Word;
  128.  
  129. BEGIN
  130. With Super do
  131.   begin
  132.   If ExistAnyFile (FileName) then
  133.     begin
  134.     WriteLn ('Initializing ', FileName);
  135.     Assign (ReadFile, FileName);                 { open file }
  136.     Reset (ReadFile);
  137.  
  138.     { Count number of entries in file }
  139.     Count := 0;
  140.     Size  := 0;
  141.     While not EOF (ReadFile) do
  142.       begin
  143.       ReadLn (ReadFile, ReadStr);
  144.       If ReadStr > '' then                       { skip blank strings }
  145.         If ReadStr [1] <> '{' then               { skip comments }
  146.           begin
  147.           inc (Count);                           { count number of entries }
  148.           inc (Size, Length (ReadStr));          { add length }
  149.           end;
  150.       end;
  151.     GetMem (EntryPtr, Size);                     { memory for superstring }
  152.     GetMem (ArrayPtr, 2 * Count + 4);            { memory for pointers }
  153.     ArrayPtr^ [0] := Count;
  154.  
  155.     Reset (ReadFile);                            { go to start of file }
  156.     Count := 1;
  157.     Size  := 0;
  158.     While not EOF (ReadFile) do
  159.       begin
  160.       ReadLn (ReadFile, ReadStr);
  161.       If ReadStr > '' then                       { skip blank strings }
  162.         If ReadStr [1] <> '{' then               { skip comments }
  163.           begin
  164.           ArrayPtr^ [Count] := succ (Size);      { determine start of entry }
  165.           move (ReadStr [1],
  166.                 EntryPtr^ [ArrayPtr^ [Count]],
  167.                 Length (ReadStr));               { store entry }
  168.           inc (Count);                           { add to count }
  169.           inc (Size, Length (ReadStr));          { add to size }
  170.           end;
  171.       end;
  172.     ArrayPtr^ [Count] := succ (Size);            { determine start of entry }
  173.     Close (ReadFile);
  174.     end
  175.   else
  176.     begin
  177.     EntryPtr := nil;
  178.     ArrayPtr := nil;
  179.     Size := 0;
  180.     end;
  181.   end;
  182. END;
  183.  
  184. { ========================================================================= }
  185.  
  186. (*
  187. Once all the data is stored in a superstring, it can be instantly accessed
  188. by a call to the GetWord function:
  189.  
  190. S := GetWord (Super, Num);
  191.  
  192. This will access the superstring and pull out the numth entry.  The function
  193. GetWord will not return a string unless there is a valid entry.
  194.  
  195. First, it determines Len (the length of the desired word) by subtracting
  196. the starting location of the word from the starting location of the
  197. subsequent word.  (The total length of the SuperString is stored in the
  198. last byte of the array pointed to by ArrayPtr^, so that the last word
  199. is also accessible.)  The value of Len is automatically stored in S[0].
  200.  
  201. Then, having determined the length of the numth word, it moves that many
  202. characters from EntryPtr^ [ArrayPtr [num]] to S[1], and returns S.
  203. *)
  204.  
  205. { ========================================================================= }
  206.  
  207. FUNCTION GetWord (Super : SuperString;  Num : Word) : String;
  208.  
  209. VAR
  210.   S   : String;
  211.   Len : Byte absolute S;                         { the length byte of S }
  212.  
  213. BEGIN
  214. With Super do
  215.   begin
  216.   If
  217.     (Size = 0) or (ArrayPtr = nil) or (EntryPtr = nil)
  218.       or
  219.     (Num > ArrayPtr^ [0])
  220.   then
  221.     GetWord := ''
  222.   else
  223.     begin
  224.     Len := ArrayPtr^ [Succ (Num)] - ArrayPtr^ [Num];   { get its length }
  225.     move (EntryPtr^ [ArrayPtr^ [Num]], S [1], Len);    { move word to string }
  226.     GetWord := S;
  227.     end;
  228.   end;
  229. END;
  230.  
  231. { ========================================================================= }
  232.  
  233. PROCEDURE DisposeSuperString (Super : SuperString);
  234. Begin
  235. With Super do
  236.   begin
  237.   If ArrayPtr <> nil then FreeMem (ArrayPtr, Succ (ArrayPtr^ [0]));
  238.   If EntryPtr <> nil then FreeMem (EntryPtr, Size);
  239.   ArrayPtr := nil;
  240.   EntryPtr := nil;
  241.   Size := 0;
  242.   end;
  243. end;
  244.  
  245. { ========================================================================= }
  246.  
  247. VAR
  248.   OrdinalSet,
  249.   GreekSet   : SuperString;
  250.  
  251. { ========================================================================= }
  252.  
  253. PROCEDURE InitFiles;
  254.  
  255. BEGIN
  256. BuildSuperString ('Ordinal.Dat', OrdinalSet);
  257. BuildSuperString ('Greek.Dat', GreekSet);
  258. END;
  259.  
  260. { ========================================================================= }
  261.  
  262. (*
  263. QuitProgram demonstrates how to reclaim the heap memory.  When the program
  264. quits, the heap memory is automatically returned to DOS, of course;  but
  265. if you need to release memory before the end of the program, use
  266. DisposeSuperString or QuitProgram;
  267. *)
  268.  
  269. { ========================================================================= }
  270.  
  271. PROCEDURE QuitProgram;
  272.  
  273. BEGIN
  274. DisposeSuperString (GreekSet);
  275. DisposeSuperString (OrdinalSet);
  276. END;
  277.  
  278. { ========================================================================= }
  279.  
  280. VAR
  281.   Loop : Word;
  282.  
  283. BEGIN
  284. ClrScr;
  285. InitFiles;                                       { read files into memory }
  286. WriteLn;
  287.  
  288. For Loop := 1 to 12 do
  289.   If
  290.     (Loop <= OrdinalSet.ArrayPtr^ [0]) and (Loop <= GreekSet.ArrayPtr^ [0])
  291.   then
  292.     WriteLn ('The ', GetWord (OrdinalSet, Loop),
  293.              ' letter of the Greek alphabet is ',
  294.              GetWord (GreekSet, Loop),'.');
  295.  
  296. QuitProgram;                                     { discard heap memory }
  297. END.
  298.  
  299. { ========================================================================= }
  300.  
  301.